package com.apobates.forum.nanus.core.cookie;

import com.apobates.forum.nanus.NanusBean;
import com.apobates.forum.nanus.NanusConverter;
import com.apobates.forum.nanus.NanusHashStrategy;
import com.apobates.forum.nanus.NanusStorageConfig;
import com.apobates.forum.nanus.core.*;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * Cookie的存储方案
 * @author xiaofanku@live.cn
 * @since 20210827
 */
public class NanusCookieStorageProvider extends AbstractNanusStorage {
    private final static Logger logger = LoggerFactory.getLogger(NanusCookieStorageProvider.class);

    public NanusCookieStorageProvider(NanusConverter beanConverter, NanusStorageConfig expirationConfig, NanusHashStrategy hashStrategy) {
        super(beanConverter, expirationConfig, hashStrategy);
    }

    @Override
    public void store(NanusBean bean, HttpServletRequest request, HttpServletResponse response) {
        NanusStorageConfig config = getStorageConfig();
        //过期日期
        LocalDateTime expireDate = config.getUnit().plusDateTime(config.getDuration(), LocalDateTime.now());
        //计算载荷
        Map<String,String> payload = getBeanConverter().toMap(bean);
        String serialJSON = new Gson().toJson(payload);
        //int stamp, String factor, String hash
        String ss = new NanusStoragePayload(
                getUnixStamp(expireDate),
                getFactor(request),
                encode(serialJSON, NP)).toString();

        if (StringUtils.isNotBlank(ss)) {
            serializeCookie(
                    ss,
                    expireDate,
                    request,
                    response,
                    config.getKeyName(),
                    config.getPath(),
                    getCookieDomain(),
                    config.isHttps());
        }
    }

    @Override
    public void delete(HttpServletRequest request, HttpServletResponse response) {
        NanusStorageConfig config = getStorageConfig();
        expireCookie(request, response, config.getKeyName(), config.getPath(), getCookieDomain());
    }

    @Override
    public Optional<NanusBean> getInstance(HttpServletRequest request) {
        try {
            NanusStoragePayload nsp = parsePayload(request).orElse(null);
            if(null == nsp){
                return Optional.empty();
            }
            //是否到期了
            if(nsp.getStamp() < getUnixStamp()){
                return Optional.empty();
            }
            Map<String,String> payload = toPayloadMap(nsp);
            return Optional.ofNullable(getBeanConverter().toBean(payload));
        }catch (Exception e){
            if(logger.isDebugEnabled()){
                logger.debug("[Nanus]get cookie msb fail, exception: "+e.getMessage(), e);
            }
        }
        return Optional.empty();
    }

    /**
     * 抽像出getInstance和refresh共用的代码段
     * @param request
     * @return
     */
    @Override
    protected Optional<NanusStoragePayload> parsePayload(HttpServletRequest request) {
        NanusStorageConfig config = getStorageConfig();
        //还原出载荷
        Cookie cookie = CookieUtils.queryCookie(request, config.getKeyName()).orElse(null);
        if (null == cookie) {
            return Optional.empty();
        }

        NanusStoragePayload nsp = NanusStoragePayload.getInstance(cookie.getValue());
        return Optional.ofNullable(nsp);
    }

    /**
     * 抽像出getInstance和refresh共用的代码段
     * @param nsp
     * @return
     */
    @Override
    protected Map<String,String> toPayloadMap(NanusStoragePayload nsp){
        Objects.requireNonNull(nsp);
        String serialJSON = decode(nsp.getHash(), NP);
        Map<String,String> payload = new Gson().fromJson(serialJSON, new TypeToken<Map<String,String>>(){}.getType());
        return payload;
    }

    @Override
    public void refresh(HttpServletRequest request, HttpServletResponse response, NanusBean newBean) {
        NanusStoragePayload currentNsp = parsePayload(request).orElse(null);
        Map<String, String> newPayload = mergeBeanMap(toPayloadMap(currentNsp), newBean);
        if(newPayload.isEmpty()){
            return;
        }
        String serialJSON = new Gson().toJson(newPayload);
        //使用原来的到期日期
        String ss = new NanusStoragePayload(
                currentNsp.getStamp(),
                getFactor(request),
                encode(serialJSON, NP)).toString();

        if (StringUtils.isNotBlank(ss)) {
            NanusStorageConfig config = getStorageConfig();
            //使用原来的到期日期
            serializeCookie(
                    ss,
                    getDateTimeByUnixStamp(currentNsp.getStamp()),
                    request,
                    response,
                    config.getKeyName(),
                    config.getPath(),
                    getCookieDomain(),
                    config.isHttps());
        }
    }

    @Override
    public boolean isSupportRevival() {
        return true;
    }

    /**
     * 序列化会员信息到Cookie中
     *
     * @param cookieValue cookie中保存的值
     * @param expireDateTime cookie到期的日期
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieSymbol cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     * @param isHttps work on HTTPS/true,false work on http
     */
    protected void serializeCookie(
            String cookieValue,
            LocalDateTime expireDateTime,
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieSymbol,
            String cookiePath,
            String cookieDomain,
            boolean isHttps) {
        try {
            CookieUtils.serializeCookie(cookieValue, expireDateTime, request, response, cookieSymbol, cookiePath, cookieDomain, isHttps);
        } catch (IllegalStateException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[OM][CS]write cookie fail, reason: " + e.getMessage(), e);
            }
        }
    }

    /**
     * 清空Cookie中的会员信息
     *
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieSymbol cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     */
    protected void expireCookie(
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieSymbol,
            String cookiePath,
            String cookieDomain) {
        CookieUtils.expireCookie(request, response, cookieSymbol, cookiePath, cookieDomain);
    }
}
